
// This guard ensures that POSIX implementation is used only
// on Unix platform.
#ifndef MY_USE_POSIX

    #error POSIX version of __FILE__ is used without MY_USE_UNIX defined.

#endif

#include "Concurrent/Unix/MutexImp.hpp"
#include "Debug/Assert.hpp"

namespace My
{
    MutexImp::MutexImp(bool is_recursive)

        #ifdef MY_USE_DEBUG

            : m_Recursive(is_recursive),
              m_Locks(0)

        #endif

    {
        int ret = 0;

        // Initializing POSIX mutex attributes.
        pthread_mutexattr_t attributes;

        ret = pthread_mutexattr_init(&attributes);
        MY_ASSERT(!ret, "pthread_mutexattr_init() returned %d.", ret);

        if(is_recursive)
            ret = pthread_mutexattr_settype(&m_Mutex, PTHREAD_MUTEX_RECURSIVE);
        else
            ret = pthread_mutexattr_settype(&_Mutex, PTHREAD_MUTEX_NORMAL);
        MY_ASSERT(!ret, "pthread_mutexattr_settype() returned %d.", ret);

        ret = pthread_mutex_init(&m_Mutex, &attributes);
        MY_ASSERT(!ret, "pthread_mutex_init() returned %d", ret);
    }

    MutexImp::~MutexImp()
    {
        MY_ASSERT(m_Locks == 0,"Mutex::~Mutex() called on locked Mutex.");

        int ret = pthread_mutex_destroy(&m_Mutex);
        MY_ASSERT(!ret, "pthread_mutex_destroy() returned %d.", ret);
    }

    void MutexImp::Lock()
    {
        MY_ASSERT(m_Recursive || m_Locks == 0, "Mutex::Lock() called on locked and non-recursive mutex.");

        int ret = pthread_mutex_lock(&m_Mutex);
        MY_ASSERT(!ret, "pthread_mutex_lock() returned %d.", ret);

        #ifdef MY_USE_DEBUG

            if(m_Recursive)
                ++m_Locks;
            else
                m_Locks = 1;

        #endif
    }

    bool MutexImp::TryLock()
    {
        int ret = pthread_mutex_trylock(&m_Mutex);
        MY_ASSERT(!ret || ret == EBUSY, "Mutex trylock failed.", ret);

        #ifdef MY_USE_DEBUG

            if(!ret && (m_Recursive || m_Locks == 0))
                ++m_Locks;

        #endif

        return !ret;
    }

    void MutexImp::Unlock()
    {
        #ifdef MY_USE_DEBUG

            --m_Locks;

        #endif

        int ret = pthread_mutex_unlock(&m_Mutex);
        MY_ASSERT(!ret, "pthread_mutex_unlock() returned %d.", ret);
    }

    void MutexImp::Init()
    {

    }
}
